﻿namespace Jim.OA;

/// <summary>
/// 泛微OA Restful Api客户端
/// </summary>
public  class OARestClient:SdkClient
{
    public string AppKey { get; private set; }
    public string Cpk { get; private set; }
    public string Secret { get; private set; }
    public string Spk { get; private set; }
    public string Token { get; private set; }

    public OARestClient(string appKey, string baseUrl) : this(appKey, baseUrl, "", "", "", "") { }

    public OARestClient(string appKey,string baseUrl,string cpk,string secret,string spk ,string token)
    {
        if (string.IsNullOrEmpty(appKey)) throw new ArgumentException("AppKey不能为空");
        if (string.IsNullOrEmpty(baseUrl)) throw new ArgumentException("BaseUrl不能为空");
        AppKey = appKey;
        ServerUrl = baseUrl;
        Cpk = cpk;
        Secret = secret;
        Spk = spk;
        Token = token;
        httpHelper = new HttpHelper();
    }

    /// <summary>
    /// 向OA发起请求
    /// </summary>
    /// <typeparam name="T">返回对象，需要继承OAResponse</typeparam>
    /// <param name="request">请求参数，需要建成ISdk<T></param>
    /// <returns></returns>
    public override async Task<T> Execute<T>(ISdkRequest<T> request)
    {
        if (!request.GetRequestAttr(out SdkAttribute oaReqAttr)) 
            return Error<T>($"Input对象缺少SdkAttribute属性，请联系管理员");  
        //if (request is not IOALogin && request is not IOASSO && Token.IsNullOrEmpty() ) await TryLoginAsync();  
        try
        {
            // 调用接口
            var url = BuildUrl(request);
            Log($"请求地址:{url}");
            //组装请求头
            Dictionary<string, string> headers = BuildHeader(request);
            Log($"请求头文件:{headers.ToJson()}");
            var postData = BuildPostDataDic(request,false,new List<string>(){"userId"});
            Log($"post数据:{postData.ToJson()}");
            HttpItem item = new HttpItem()
            {
                Method = oaReqAttr.Method,
                Header = headers,
                URL = url,
                PostDataType = PostDataType.Form,
                PostdataDic = postData
            };
            var httpResult = await httpHelper.GetHtml(item);
            var json = httpResult.Html;

            Log($"返回信息:{json}");
            T rsp = BuildResponse<T>(request, json);
            Log($"返回对象序列化:{rsp.ToJson()}");
            return rsp;
        }
        catch (Exception e)
        {
            return Error<T>($"服务端错误:{e.Message}");
        }
    }

    /// <summary>
    /// 返回对象处理
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="value"></param>
    /// <returns></returns>
    protected override T BuildResponse<T>(ISdkRequest request,string value)
    {
        string msg = string.Empty;
        string code = "200";
        JToken resultToken =null;

        JToken jToken;
        try
        {
            jToken = JToken.Parse(value);
        }
        catch
        {
            return Success<T>( value);
        }
        if (jToken is JObject jObject)
        {
            if (jObject.ContainsKey("code"))
            {
                string codeToken = jObject.GetValue("code")?.Value<string>();
                if (codeToken != "0" && !code.Equals("success", StringComparison.OrdinalIgnoreCase))
                    code = codeToken;
                jObject.Remove("code");
            }

            if (jObject.ContainsKey("msg"))
            {
                msg = jObject.GetValue("msg")?.Value<string>();
                jObject.Remove("msg");
            }

            if (jObject.ContainsKey("status"))
            {
                bool statusToken = jObject.GetValue("status").Value<bool>();
                if (statusToken) code = "200";
                else
                {
                    if (code == "200") code = "500";
                }
                jObject.Remove("status");
            }

            JToken errMsgToken = jToken.SelectToken("errMsg");
            if (errMsgToken != null)
            {
                // 没有错误代码则复制错误代码为0
                if (code == "200") code = "500";
                if (jToken is JObject errObject)
                {
                    List<string> errList = new List<string>();
                    foreach (var prop in errObject.Properties())
                    {
                        errList.Add($"{prop.Name}:{prop.Value}");
                    }
                    if (errList.Count > 0)
                        msg += errList.Join(",");
                }
            }
            JToken dataToken = jToken.SelectToken("data");
            if (dataToken != null)
                resultToken = dataToken;
            else
                resultToken = jToken;
        }
        else
            resultToken = jToken;
        return Success<T>( resultToken);
    }


    /// <summary>
    /// 组装请求头
    /// </summary>
    /// <param name="request"></param>
    /// <returns></returns>
    /// <exception cref="Exception"></exception>
    protected override SdkDictionary BuildHeader(ISdkRequest request)
    {
        // 组装请求头
        SdkDictionary headers =base.BuildHeader(request);
        headers.Add("Context-Type", "application/x-www-form-urlencoded; charset=utf-8");
        // 如果是单点登录，直接返回
        if (request is IOASSO) return headers;
        // 正常API，携带用户信息
        headers.Add("appid", AppKey);
        // 如果有授权，则放上授权信息
        if (Token.IsNotNullOrEmpty())headers.Add("token", Token);
        // 如果需要指定用户，userid参数必填
        if (request is IOAUser oaUser)
        {
            if (string.IsNullOrEmpty(oaUser.UserId))
                throw new Exception("请求参数校验失败:UserId不能为空");
            else if (!string.IsNullOrEmpty(Spk))
            {
                var uId = RsaExtension.RSAEncryptJava(Spk, oaUser.UserId);
                headers.Add("userid", uId);
            }
        }
        else // 没有没有指定用户，则跳过session为1
        {

            headers.Add("skipsession", "1");
        }
        return headers;
    }


    /// <summary>
    /// Api登录
    /// </summary>
    /// <returns></returns>
    public async Task<bool> TryLoginAsync()
    {
        //  随机生成Java公钥
        if (string.IsNullOrEmpty(Cpk))Cpk = RsaExtension.RandomJavaPublicKey();
        OARegistOutput registRes = await Execute(new OARegistInput
        {
            Cpk = Cpk
        });
        if (!registRes.IsSuccess)throw new Exception(registRes.Message);
        Secret = registRes.Result.Secret;
        Spk = registRes.Result.Spk;

        string secret = RsaExtension.RSAEncryptJava(registRes.Result.Spk, registRes.Result.Secret);
        OATokenOutput tokenRes = await Execute(new OATokenInput
        {
            Secret = secret
        });
        if (!tokenRes.IsSuccess) throw new Exception(tokenRes.Message);
        Token = tokenRes.Result.Token;
        return true;
    }
}
